package org.particlethink.service.procure;

import org.particlethink.auth.DefaultUser;
import org.particlethink.context.Context;
import org.particlethink.context.DefaultContext;
import org.particlethink.entry.OrderStatus;
import org.particlethink.entry.PlanStatus;
import org.particlethink.entry.PlanTyp;
import org.particlethink.exception.BatchPlanCancelException;
import org.particlethink.exception.NoPermissionException;
import org.particlethink.exception.ParameterException;
import org.particlethink.exception.StatusException;
import org.particlethink.model.*;
import org.particlethink.request.*;
import org.particlethink.response.ImportMaterialResponse;
import org.particlethink.response.MaterialResidueResponse;
import org.particlethink.service.OrderService;
import org.particlethink.service.listen.event.SubmitBatchPlanEvent;
import org.particlethink.utils.ListUtilsKt;
import org.particlethink.utils.TimeUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
public class ProjectUserImpl extends CompanyUserImpl {//项目用户
    @Autowired
    private ApplicationEventPublisher publisher;

    @Autowired
    private OrderService orderService;

    //创建总计划
    public void saveMasterPlan(MasterPlan masterPlan, List<PlanMaterialRequest> planMaterialRequests) {

        this.masterPlanMapper.insert(masterPlan);
        if (planMaterialRequests != null && !planMaterialRequests.isEmpty()) {
            List<PlanMaterial> planMaterials = planMaterialRequests.stream().map((request) -> request.toPlanMaterial(masterPlan.getId(), PlanTyp.Master)).collect(Collectors.toList());
            this.planMaterialMapper.batchInsert(planMaterials);
        }

    }

    private String getMasterPlanNo() {
        List<MasterPlan> masterPlans = this.masterPlanMapper.selectByDuration(TimeUtil.beginTimeOfCurrentMonth(), TimeUtil.endTimeOfCurrentMonth());
        Map<String, MasterPlan> map = ListUtilsKt.map(masterPlans, MasterPlan::getNo);
        int count = masterPlans.size();
        String no = (new SimpleDateFormat("yyMM")).format(new Date()) + String.format("%03d", count + 1);

        while (map.containsKey(no)) {
            count++;
            no = (new SimpleDateFormat("yyMM")).format(new Date()) + String.format("%03d", count + 1);
        }
        return no;
    }

    //项目用户 创建总计划
    @Transactional
    public synchronized void createMasterPlan(List<Integer> projectIds, int projectUserID, MasterPlanRequest request) {
        if (request.isSubmit() && !projectIds.contains(request.getProjectId())) {
            throw new NoPermissionException("你不属于该项目");
        }

        MasterPlan masterPlan = request.toMasterPlan(projectUserID, getMasterPlanNo());
        saveMasterPlan(masterPlan, request.getPlanMaterialRequests());
        if (request.isSubmit() && masterPlan.canSubmit()) {
            submitMasterPlan(projectIds, projectUserID, masterPlan.getId());
        }
    }

    //项目用户 修改总计划
    @Transactional
    public void updateMasterPlan(List<Integer> projectIds, Integer id, MasterPlanRequest request) {

        MasterPlan masterPlan = this.masterPlanMapper.selectByPrimaryKey(id);
        if (!masterPlan.canEdit()) {
            throw new NoPermissionException("无权修改总计划");
        }

        if (!projectIds.contains(masterPlan.getProjectId())) {
            throw new NoPermissionException("你不属于该项目");
        }

        request.updateMasterPlan(masterPlan);
        this.masterPlanMapper.updateByPrimaryKeySelective(masterPlan);

        List<PlanMaterial> planMaterials = this.planMaterialMapper.selectByPlanIDAndTyp(masterPlan.getId(), PlanTyp.Master);
        Map<Integer, PlanMaterial> planMaterialMap = ListUtilsKt.map(planMaterials, PlanMaterial::getMaterialId);


        List<PlanMaterialRequest> planMaterialRequests = request.getPlanMaterialRequests();
        List<Integer> materialIds = planMaterialRequests.stream().map(PlanMaterialRequest::getMaterialId).collect(Collectors.toList());
        List<Integer> shouldDeleteMaterialIds = planMaterials.stream().map(PlanMaterial::getMaterialId).filter(materialId -> !materialIds.contains(materialId)).collect(Collectors.toList());

        if (!(masterPlan.getId() == null || masterPlan.getId() == 0 || materialIds.isEmpty() || shouldDeleteMaterialIds.isEmpty())) {
            this.planMaterialMapper.deleteByPlanIDAndTypAndMaterialIds(masterPlan.getId(), PlanTyp.Master, shouldDeleteMaterialIds);
        }
        List<PlanMaterial> savePlanMaterials = planMaterialRequests.stream().map((PlanMaterialRequest r) -> r.UpdateOrCreatePlanMaterial(planMaterialMap.get(r.getMaterialId()), masterPlan.getId(), PlanTyp.Master)).collect(Collectors.toList());

        this.planMaterialMapper.batchInsertOrUpdate(savePlanMaterials);
    }


    //项目用户 删除总计划
    @Transactional
    public void deleteMasterPlan(List<Integer> projectIds, Integer id) {
        MasterPlan masterPlan = this.masterPlanMapper.selectByPrimaryKey(id);

        if (!projectIds.contains(masterPlan.getProjectId())) {
            throw new NoPermissionException("你不属于该项目");
        }
        if (!masterPlan.canDelete()) {
            throw new StatusException("只有未提交的总计划可以删除");
        }
        this.masterPlanMapper.deleteByPrimaryKey(id);
        if (id == null || id == 0) {
            return;
        }
        this.planMaterialMapper.deleteByPlanIDAndTyp(id, PlanTyp.Master);
    }

    //提交总计划
    public void submitMasterPlan(List<Integer> projectIds, int projectUserID, int masterPlanID) {

        //查找总计划
        MasterPlan masterPlan = this.masterPlanMapper.selectByPrimaryKey(masterPlanID);
        if (!masterPlan.canSubmit()) {
            throw new StatusException();
        }

        //判断是否是该项目的用户
        if (!projectIds.contains(masterPlan.getProjectId())) {
            throw new NoPermissionException("你不是该项目用户");
        }


        masterPlan.setStatus(PlanStatus.Submitted);
        masterPlan.setSubmitId(projectUserID);
        masterPlan.setSubmitAt(new Date());

        //更改状态
        this.masterPlanMapper.updateByPrimaryKey(masterPlan);
    }

    //作废总计划
    public void discardMasterPlan(List<Integer> projectIds, int masterPlanID) {
        //查找总计划
        MasterPlan masterPlan = this.masterPlanMapper.selectByPrimaryKey(masterPlanID);

        if (!masterPlan.canDiscard()) {
            throw new StatusException("只有已提交才能作废");
        }
        //判断是否是该项目的用户
        if (!projectIds.contains(masterPlan.getProjectId())) {
            throw new NoPermissionException("你不是该项目用户");
        }


        masterPlan.setStatus(PlanStatus.Discarded);
        //总计划作废后，现有正在执行中的批次计划不受影响，继续执行。不可再新建与此总计划关联的批次计划。未提交的总计划直接删除。
        //更改状态
        this.masterPlanMapper.updateByPrimaryKey(masterPlan);
    }


    //启用总计划
    public void enableMasterPlan(List<Integer> projectIds, int masterPlanID) {

        MasterPlan masterPlan = this.masterPlanMapper.selectByPrimaryKey(masterPlanID);
        if (!masterPlan.canEnable()) {
            throw new StatusException("只有作废的才能启用");
        }
        //判断是否是该项目的用户
        if (!projectIds.contains(masterPlan.getProjectId())) {
            throw new NoPermissionException("你不是该项目用户");
        }

        masterPlan.setStatus(PlanStatus.Submitted);
        this.masterPlanMapper.updateByPrimaryKey(masterPlan);
    }


    //项目用户 修改总计划
    @Transactional
    public void updateBatchPlan(List<Integer> projectIds, Integer id, BatchPlanRequest request) {

        BatchPlan batchPlan = this.batchPlanMapper.selectByPrimaryKey(id);
        if (!batchPlan.canEdit()) {
            throw new NoPermissionException("无权修改");
        }

        if (!projectIds.contains(batchPlan.getProjectId())) {
            throw new NoPermissionException("你不属于该项目");
        }

        request.updateBatchPlan(batchPlan);
        this.batchPlanMapper.updateByPrimaryKeySelective(batchPlan);

        List<PlanMaterial> planMaterials = this.planMaterialMapper.selectByPlanIDAndTyp(batchPlan.getId(), PlanTyp.Batch);
        Map<Integer, PlanMaterial> planMaterialMap = ListUtilsKt.map(planMaterials, PlanMaterial::getMaterialId);


        List<PlanMaterialRequest> planMaterialRequests = request.getPlanMaterialRequests();
        List<Integer> materialIds = planMaterialRequests.stream().filter((planMaterialRequest) -> {
            return !planMaterialMap.containsKey(planMaterialRequest.getMaterialId());
        }).map(PlanMaterialRequest::getMaterialId).collect(Collectors.toList());
        if (!(batchPlan.getId() == null || batchPlan.getId() == 0 || materialIds.isEmpty())) {
            this.planMaterialMapper.deleteByPlanIDAndTypAndMaterialIds(batchPlan.getId(), PlanTyp.Batch, materialIds);
        }
        List<PlanMaterial> savePlanMaterials = planMaterialRequests.stream().map((PlanMaterialRequest r) -> r.UpdateOrCreatePlanMaterial(planMaterialMap.get(r.getMaterialId()), batchPlan.getId(), PlanTyp.Batch)).collect(Collectors.toList());

        this.planMaterialMapper.batchInsertOrUpdate(savePlanMaterials);
    }


    private String getBatchPlanNo(int masterPlanId, String masterPlanNo) {

        if (masterPlanNo == null || masterPlanNo.isEmpty()) {
            List<BatchPlan> batchPlans = this.batchPlanMapper.selectByDuration(TimeUtil.beginTimeOfCurrentMonth(), TimeUtil.endTimeOfCurrentMonth());
            Map<String, BatchPlan> map = ListUtilsKt.map(batchPlans, BatchPlan::getNo);
            int count = batchPlans.size();
            String no = (new SimpleDateFormat("yyMM")).format(new Date()) + "999" + String.format("%05d", count + 1);

            while (map.containsKey(no)) {
                count++;
                no = (new SimpleDateFormat("yyMM")).format(new Date()) + "999" + String.format("%05d", count + 1);
            }
            return no;
        }

        List<BatchPlan> batchPlans = this.batchPlanMapper.selectByMasterPlanID(masterPlanId);
        Map<String, BatchPlan> map = ListUtilsKt.map(batchPlans, BatchPlan::getNo);
        int count = batchPlans.size();


        String no = masterPlanNo + String.format("%05d", count + 1);
        ;

        while (map.containsKey(no)) {
            count++;
            no = masterPlanNo + String.format("%05d", count + 1);
        }

        return no;
    }


    private void saveBatchPlan(BatchPlan batchPlan, List<PlanMaterialRequest> planMaterialRequests) {
        //判断总计划是否作废

        //保存数据库
        this.batchPlanMapper.insert(batchPlan);
        List<PlanMaterial> planMaterials = planMaterialRequests.stream().map((request) -> request.toBatchPlanMaterial(batchPlan.getId(), PlanTyp.Batch)).collect(Collectors.toList());

        this.planMaterialMapper.batchInsert(planMaterials);
    }

    //创建批次计划
    @Transactional
    public synchronized void createBatchPlan(User user, BatchPlanRequest request) {
        if (!user.getProjectIds().contains(request.getProjectId())) {
            throw new NoPermissionException("只可创建/修改所属项目下的批次/总计划信息");
        }

        List<PlanMaterialRequest> planMaterialRequests = request.getPlanMaterialRequests();


        MasterPlan masterPlan = null;
        String masterPlanNo = null;
        //确认过创建批次计划的所有物料都只能是同一个物料大类
        if (request.getMasterPlanId() != 0) {
            masterPlan = this.masterPlanMapper.selectByPrimaryKey(request.getMasterPlanId());
            if (!masterPlan.canCreateBatchPlan()) {
                throw new StatusException("总计划未提交！");
            }
            masterPlanNo = masterPlan.getNo();
        }

        Material material = null;
        String unitStr = "";
        if (planMaterialRequests != null && !planMaterialRequests.isEmpty()) {
            int materialId = planMaterialRequests.get(0).getMaterialId();
            material = this.materialMapper.selectById(user.getToken(), materialId);
            List<Integer> materialIds = request.getPlanMaterialRequests().stream().map(PlanMaterialRequest::getMaterialId).collect(Collectors.toList());
            List<Material> materials = this.materialMapper.selectByIds(user.getToken(), materialIds);
            unitStr = materials.stream().map(Material::getUnit).distinct().collect(Collectors.joining(","));

        }


        String batchPlanNo = getBatchPlanNo(request.getMasterPlanId(), masterPlanNo);
        BatchPlan batchPlan = new BatchPlan(
                request.getProjectId(),
                request.getMasterPlanId(),
                masterPlan == null ? "" : masterPlan.getNo(),
                request.getDeadlineAt(),
                request.getRemark(),
                user.getUserId(),
                request.getAttachment(),
                request.getAttachmentName(),
                material == null ? "" : material.getParentCategoryId(),
                request.expectNum(),
                unitStr,
                batchPlanNo
        );


        saveBatchPlan(batchPlan, planMaterialRequests);

        if (request.isSubmit() && batchPlan.canSubmit()) {
            this.submitBatchPlan(user.getProjectIds(), user.getUserId(), batchPlan.getId());
        }
    }


    //提交批次计划
    @Transactional
    public void submitBatchPlan(List<Integer> projectIds, int projectUserID, int batchPlanID) {

        BatchPlan batchPlan = this.batchPlanMapper.selectByPrimaryKey(batchPlanID);

        if (!batchPlan.canSubmit()) {
            throw new StatusException();
        }

        if (!projectIds.contains(batchPlan.getProjectId())) {
            throw new NoPermissionException("只可创建/修改所属项目下的批次/总计划信息");
        }

        batchPlan.setStatus(PlanStatus.Submitted);
        batchPlan.setSubmitId(projectUserID);
        batchPlan.setSubmitAt(new Date());

        //更新总计划的批次计划数量
        if (batchPlan.getMasterPlanId() != 0) {
            MasterPlan masterPlan = this.masterPlanMapper.selectByPrimaryKey(batchPlan.getMasterPlanId());
            masterPlan.setBatchPlanCount(masterPlan.getBatchPlanCount() + 1);
            this.masterPlanMapper.updateByPrimaryKey(masterPlan);
        }


        //更改状态
        this.batchPlanMapper.updateByPrimaryKey(batchPlan);

        // 分发订单
        List<PlanMaterial> planMaterials = this.planMaterialMapper.selectByPlanIDAndTyp(batchPlanID, PlanTyp.Batch);
        HashMap<Integer, List<PlanMaterial>> supplierIdMap = new HashMap<>();
        for (PlanMaterial planMaterial : planMaterials) {

            Integer supplierId = planMaterial.getSupplierId();
            List<PlanMaterial> planMaterials1;
            if (supplierIdMap.containsKey(supplierId)) {
                planMaterials1 = supplierIdMap.get(supplierId);
            } else {
                planMaterials1 = new ArrayList<>();
                supplierIdMap.put(supplierId, planMaterials1);
            }
            planMaterials1.add(planMaterial);
        }

        for (Map.Entry<Integer, List<PlanMaterial>> entry : supplierIdMap.entrySet()) {
            createBatchOrder(batchPlan, entry.getKey(), entry.getValue());
        }

        // 触发自定义事件
        SubmitBatchPlanEvent customEvent = new SubmitBatchPlanEvent(batchPlan.getMasterPlanId());
        publisher.publishEvent(customEvent);
    }

    private void createBatchOrder(BatchPlan batchPlan, Integer supplierId, List<PlanMaterial> planMaterials) {


//
        Order order = new Order(batchPlan.getId(), batchPlan.getNo(), batchPlan.getMasterPlanId(), batchPlan.getMasterPlanNo(), supplierId, batchPlan.getCategoryId(), batchPlan.getDeadlineAt(), batchPlan.getSubmitId(), batchPlan.getProjectId());
        this.orderMapper.insert(order);

        List<Integer> planMaterialIds = planMaterials.stream().map(PlanMaterial::getId).collect(Collectors.toList());

        this.planMaterialMapper.BatchUpdateOrderID(order.getId(), planMaterialIds);
    }


    //作废批次计划
    public void discardBatchPlan(List<Integer> projectIds, int batchPlanID) {
        //查找批次计划
        BatchPlan batchPlan = this.batchPlanMapper.selectByPrimaryKey(batchPlanID);
        if (!batchPlan.canDiscard()) {
            throw new StatusException("已提交才能作废");
        }
        if (!projectIds.contains(batchPlan.getProjectId())) {
            throw new NoPermissionException("你不属于该项目");
        }


        // 关联订单状态是否为“进行中”/“已完成” 不得作废

        ArrayList<OrderStatus> statuses = new ArrayList<>();
        statuses.add(OrderStatus.InProgress);
        statuses.add(OrderStatus.Done);

        List<Order> orders = this.orderMapper.selectByBatchPlanIdAndStatuses(batchPlanID, statuses);
        if (!orders.isEmpty()) {
            throw new BatchPlanCancelException();
        }
//        更新批次计划状态为“已作废”
        batchPlan.setStatus(PlanStatus.Discarded);
        this.batchPlanMapper.updateByPrimaryKey(batchPlan);


//        删除关联订单
        this.orderMapper.deleteByBatchPlanId(batchPlanID);
    }


    //启用批次计划
    @Transactional
    public void enableBatchPlan(List<Integer> projectIds, int batchPlanID) {
        //查找批次计划
        BatchPlan batchPlan = this.batchPlanMapper.selectByPrimaryKey(batchPlanID);
        if (!batchPlan.canEnable()) {
            throw new StatusException("已废弃才能启用");
        }
        if (!projectIds.contains(batchPlan.getProjectId())) {
            throw new NoPermissionException("你不属于该项目");
        }


//        更新批次计划状态为“已作废”
        batchPlan.setStatus(PlanStatus.UnDisposed);
        this.batchPlanMapper.updateByPrimaryKey(batchPlan);

        submitBatchPlan(projectIds, batchPlan.getSubmitId(), batchPlanID);
    }


    //撤回批次计划
    @Transactional
    public void cancelBatchPlan(List<Integer> projectIds, int batchPlanID) {
        //查找批次计划
        BatchPlan batchPlan = this.batchPlanMapper.selectByPrimaryKey(batchPlanID);
        if (!batchPlan.canCancel()) {
            throw new StatusException("已提交才能测回");
        }

        if (!projectIds.contains(batchPlan.getProjectId())) {
            throw new NoPermissionException("你不属于该项目");
        }


        // 关联订单状态是否为“进行中”/“已完成” 不得作废

        ArrayList<OrderStatus> statuses = new ArrayList<>();
        statuses.add(OrderStatus.InProgress);
        statuses.add(OrderStatus.Done);

        List<Order> orders = this.orderMapper.selectByBatchPlanIdAndStatuses(batchPlanID, statuses);
        if (!orders.isEmpty()) {
            throw new BatchPlanCancelException();
        }
//        更新批次计划状态为“已作废”
        batchPlan.setStatus(PlanStatus.UnDisposed);
        this.batchPlanMapper.updateByPrimaryKey(batchPlan);


//        删除关联订单
        this.orderMapper.deleteByBatchPlanId(batchPlanID);
    }


    //删除批次计划
    @Transactional
    public void deleteBatchPlan(List<Integer> projectIds, int batchPlanID) {


        BatchPlan batchPlan = this.batchPlanMapper.selectByPrimaryKey(batchPlanID);
        if (!projectIds.contains(batchPlan.getProjectId())) {
            throw new NoPermissionException("你不属于该项目");
        }
        if (!batchPlan.canDelete()) {
            throw new StatusException("只有待提交的才能删除");
        }

        this.batchPlanMapper.deleteByPrimaryKey(batchPlan.getId());
        this.planMaterialMapper.deleteByPlanIDAndTyp(batchPlan.getId(), PlanTyp.Batch);
    }


    //撤回订单
    public void cancelOrder(List<Integer> projectIds, int orderID) {
        orderService.refuse(orderID, null, (order) -> projectIds.contains(order.getProjectId()));

    }

    //备注订单
    public void remarkOrder(List<Integer> projectIds, int orderID, String remark) {
        Order order = this.orderMapper.selectByPrimaryKey(orderID);
        if (!order.canRemark()) {
            return;
        }
        if (!projectIds.contains(order.getProjectId())) {
            throw new NoPermissionException("你不属于该项目");
        }
        order.setRemark(remark);

        this.orderMapper.updateByPrimaryKeySelective(order);
    }

    //重新发起
    public void reCreateOrder(List<Integer> projectIds, Integer supplierId, int orderID) {
        Order order = this.orderMapper.selectByPrimaryKey(orderID);
        if (!projectIds.contains(order.getProjectId())) {
            throw new NoPermissionException();
        }
        if (!order.canReCreate()) {
            throw new StatusException();
        }
        order.setStatus(OrderStatus.Unshipped);
        order.setSupplierId(supplierId);
        this.orderMapper.updateByPrimaryKeySelective(order);
    }

    //终止订单发货
    @Transactional
    public void stopDelivery(List<Integer> projectIds, int orderID) {
        Order order = this.orderMapper.selectByPrimaryKey(orderID);

        if (order == null) {
            return;
        }
        if (!order.canStop()) {
            throw new StatusException();
        }

        if (!projectIds.contains(order.getProjectId())) {
            throw new NoPermissionException();
        }
        List<Deliver> delivers = this.deliverMapper.selectByOrderId(orderID);
        order.setStopped(true);
        orderService.complete(order, delivers);


        //终止发货后，要做判断。如果存在关联子订单的状态均已是“已入库”、“已对账”、“已作废”的，修改状态为已完成。else，不修改状态
        //终止发货后，修改订单里边的所有剩余未发数量为0哇


    }

    public List<MaterialResidueResponse> residue(Integer id) {
        List<PlanMaterial> planMaterials = this.planMaterialMapper.selectByPlanIDAndTyp(id, PlanTyp.Master);

        return planMaterials.stream().map(planMaterial -> {
            return new MaterialResidueResponse(planMaterial.getMaterialId(), planMaterial.getExpectNum() - planMaterial.getActualNum());
        }).collect(Collectors.toList());
    }

    public List<ImportMaterialResponse> ImportMaterialPlan(User user,List<MasterBatchMaterialRequest> requests) {
        Context ctx = DefaultContext.Builder.background().withValue("token", ((DefaultUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUser().getToken());
        List<String> materialNos = requests.stream().map(MasterBatchMaterialRequest::getMaterialNo).collect(Collectors.toList());

        List<Material> materials = this.materialMapper.selectByNos(user.getToken(), materialNos);
        Map<String, Material> materialMap = ListUtilsKt.map(materials, Material::getNo);
        List<String> categoryIds = materials.stream().flatMap(plan -> Stream.of(plan.getCategoryId(), plan.getParentCategoryId())).distinct().collect(Collectors.toList());

        List<Category> categories = this.categoryMapper.selectByIds(user.getToken(), categoryIds);
        Map<String, Category> categoryMap = ListUtilsKt.map(categories, Category::getId);

        List<String> supplierNos = requests.stream().map(MasterBatchMaterialRequest::getSupplierNo).filter(t -> {
            return t != null && !t.isEmpty();
        }).collect(Collectors.toList());

        Map<String, Supplier> supplierMap;
        if (supplierNos != null && !supplierNos.isEmpty()) {
            List<Supplier> suppliers = this.supplierMapper.selectByNos(user.getToken(), supplierNos);
            supplierMap = ListUtilsKt.map(suppliers, Supplier::getNo);
        } else {
            supplierMap = new HashMap<>();
        }


        return requests.stream().map(request -> {
            Material material = materialMap.get(request.getMaterialNo());
            return new ImportMaterialResponse(material, categoryMap.get(material.getParentCategoryId()), categoryMap.get(material.getCategoryId()), Integer.parseInt(request.getExpectNum()), request.getRemark(), supplierMap.get(request.getSupplierNo()), request.getPurpose());
        }).collect(Collectors.toList());
    }
}

